﻿using DotNetCore.CAP.Messages;
using Framework.Core;
using Framework.Core.JsonSerialization;
using Polly;
using Polly.Extensions.Http;
using Polly.Retry;
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Mime;
using System.Net.Security;
using System.Reflection.PortableExecutable;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Framework.ExtensionsMods.RemoteRequest
{
	/// <summary>
	/// Http远程请求操作帮助类
	/// </summary>
	public class HttpHelper
	{
		private IHttpClientFactory _HttpClientFactory { get; }
		private HttpClient _HttpClient;

		public HttpHelper(IHttpClientFactory httpClientFactory)
		{
			_HttpClientFactory = httpClientFactory;
		}

		/// <summary>
		/// 发起POST同步请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="httpItem"></param>
		/// <returns></returns>
		public string HttpPost(string url, HttpItem httpItem)
		{
			_HttpClient = _HttpClientFactory.CreateClient();

			SetRequest(httpItem);

			using (HttpContent httpContent = new StringContent(httpItem.Postdata, httpItem.PostEncoding))
			{
				if (httpItem.ContentType != null)
					httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(httpItem.ContentType);
				
				HttpResponseMessage response = _HttpClient.PostAsync(url, httpContent).Result;
				return response.Content.ReadAsStringAsync().Result;
			}
		}

		/// <summary>
		/// 发起POST异步请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="httpItem"></param>
		/// <returns></returns>
		public async Task<string> HttpPostAsync(string url, HttpItem httpItem)
		{
			_HttpClient = _HttpClientFactory.CreateClient();

			SetRequest(httpItem);

			using (HttpContent httpContent = new StringContent(httpItem.Postdata, httpItem.PostEncoding))
			{
				if (httpItem.ContentType != null)
					httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(httpItem.ContentType);

				var policy = GetRetryPolicy();
				HttpResponseMessage response = await policy.ExecuteAsync(() => _HttpClient.PostAsync(url, httpContent));
				return await response.Content.ReadAsStringAsync();
			}
		}

		/// <summary>
		/// 发起GET同步请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="headers"></param>
		/// <param name="contentType"></param>
		/// <returns></returns>
		public string HttpGet(string url, HttpItem httpItem)
		{
			_HttpClient = _HttpClientFactory.CreateClient();

			SetRequest(httpItem);

			if (httpItem.ContentType != null)
				_HttpClient.DefaultRequestHeaders.Add("ContentType", httpItem.ContentType);

			HttpResponseMessage response = _HttpClient.GetAsync(url).Result;
			return response.Content.ReadAsStringAsync().Result;
		}

		/// <summary>
		/// 发起GET异步请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="headers"></param>
		/// <param name="contentType"></param>
		/// <returns></returns>
		public async Task<string> HttpGetAsync(string url, HttpItem httpItem)
		{
			_HttpClient = _HttpClientFactory.CreateClient();

			SetRequest(httpItem);

			if (httpItem.ContentType != null)
				_HttpClient.DefaultRequestHeaders.Add("ContentType", httpItem.ContentType);

			var policy = GetRetryPolicy();
			HttpResponseMessage response = await policy.ExecuteAsync(() => _HttpClient.GetAsync(url));
			return await response.Content.ReadAsStringAsync();
		}
		
		/// <summary>
		/// 发起POST同步请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="postData"></param>
		/// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
		/// <param name="headers">填充消息头</param>
		/// <returns></returns>
		public T HttpPost<T>(string url, HttpItem httpItem)
		{
			return JSON.Deserialize<T>(HttpPost(url, httpItem));
		}

		/// <summary>
		/// 发起POST异步请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="postData"></param>
		/// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
		/// <param name="headers">填充消息头</param>
		/// <returns></returns>
		public async Task<T> HttpPostAsync<T>(string url, HttpItem httpItem)
		{
			return JSON.Deserialize<T>(await HttpPostAsync(url, httpItem));
		}

		/// <summary>
		/// 发起GET同步请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="headers"></param>
		/// <param name="contentType"></param>
		/// <returns></returns>
		public T HttpGet<T>(string url, HttpItem httpItem)
		{
			return JSON.Deserialize<T>(HttpGet(url, httpItem));
		}

		/// <summary>
		/// 发起GET异步请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="headers"></param>
		/// <param name="contentType"></param>
		/// <returns></returns>
		public async Task<T> HttpGetAsync<T>(string url, HttpItem httpItem)
		{
			var res = await HttpGetAsync(url, httpItem);
			return JSON.Deserialize<T>(res);
		}

		/// <summary>
		/// 一个包含文本字段和文件的form-data请求
		/// </summary>
		/// <param name="url"></param>
		/// <param name="textFieldKey"></param>
		/// <param name="textFieldValue"></param>
		/// <param name="fileStream"></param>
		/// <param name="fileName"></param>
		/// <param name="contentType"></param>
		/// <returns></returns>
		public async Task<HttpResponseMessage> SubmitFormDataAsync(string url, string textFieldKey, string textFieldValue, Stream fileStream, string fileName, string contentType)
		{
			_HttpClient = _HttpClientFactory.CreateClient();
			using (var formData = new MultipartFormDataContent())
			{
				// 添加文本字段
				formData.Add(new StringContent(textFieldValue), textFieldKey);

				// 添加文件
				var fileContent = new StreamContent(fileStream);
				fileContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(contentType);
				formData.Add(fileContent, "file", Path.GetFileName(fileName));

				// 发送请求
				var response = await _HttpClient.PostAsync(url, formData);

				return response;
			}
		}

		/// <summary>
		/// 下载网络资源
		/// </summary>
		/// <param name="url"></param>
		/// <param name="localPath"></param>
		/// <param name="action"></param>
		/// <param name="mode"></param>
		/// <returns></returns>
		public async Task DownloadFileAsync(string url, string localPath, Action<long, long> action, FileMode mode)
		{
			_HttpClient = _HttpClientFactory.CreateClient();

			using (HttpResponseMessage response = await _HttpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
			{
				using (Stream stream = await response.Content.ReadAsStreamAsync())
				{
					using (FileStream fileStream = new FileStream(localPath, mode, FileAccess.Write, FileShare.None, 8192, true))
					{
						byte[] buffer = new byte[8192];
						int bytesRead;
						long totalBytesRead = 0;
						long totalBytes = response.Content.Headers.ContentLength ?? -1;

						while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
						{
							await fileStream.WriteAsync(buffer, 0, bytesRead);
							totalBytesRead += bytesRead;
							action?.Invoke(totalBytesRead, totalBytes);     // 返回实时的下载进度
						}
					}
				}
			}
		}

		/// <summary>
		/// 为请求准备参数
		/// </summary>
		///<param name="item">参数列表</param>
		private void SetRequest(HttpItem item)
		{
			//设置Header参数
			if (item.Header != null && item.Header.Count > 0)
			{
				foreach (var header in item.Header)
					_HttpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
			}

			// 设置代理
			SetProxy(item);

			if (item.Protocol != null) _HttpClient.DefaultRequestHeaders.Protocol = item.Protocol;

			_HttpClient.Timeout = item.Timeout;

			if (!string.IsNullOrWhiteSpace(item.Host))
			{
				_HttpClient.DefaultRequestHeaders.Host = item.Host;
			}

			//Accept
			_HttpClient.DefaultRequestHeaders.Add("Accept", item.Accept);

			// 长连接
			if(item.KeepAlive)
			{
				_HttpClient.DefaultRequestHeaders.Connection.Add("keep-alive");
			}

			//UserAgent客户端的访问类型，包括浏览器版本和操作系统信息
			_HttpClient.DefaultRequestHeaders.Add("User-Agent", item.UserAgent);

			//// 编码
			//encoding = item.Encoding;

			//设置Cookie
			if(item.Cookie != null)
			{
				_HttpClient.DefaultRequestHeaders.Add("Cookie", item.Cookie);
			}

			//来源地址
			if (item.Referer != null)
			{
				_HttpClient.DefaultRequestHeaders.Referrer = new Uri(item.Referer);
			}
			
		}

		/// <summary>
		/// 设置代理
		/// </summary>
		/// <param name="item">参数对象</param>
		private void SetProxy(HttpItem item)
		{
			if (!string.IsNullOrWhiteSpace(item.ProxyIp))
			{
				//设置代理服务器
				if (item.ProxyIp.Contains(":"))
				{
					string[] plist = item.ProxyIp.Split(':');
					WebProxy myProxy = new WebProxy(plist[0].Trim(), Convert.ToInt32(plist[1].Trim()));
					myProxy.BypassProxyOnLocal = true;

					// 如果有用户名和密码需要以下设置
					if (!string.IsNullOrWhiteSpace(item.ProxyUserName) && !string.IsNullOrWhiteSpace(item.ProxyPwd))
					{
						myProxy.Credentials = new NetworkCredential(item.ProxyUserName, item.ProxyPwd);
					}
					//给当前请求对象
					HttpClient.DefaultProxy = myProxy;
				}
				else
				{
					WebProxy myProxy = new WebProxy(item.ProxyIp, false);
					myProxy.BypassProxyOnLocal = true;

					// 如果有用户名和密码需要以下设置
					if (!string.IsNullOrWhiteSpace(item.ProxyUserName) && !string.IsNullOrWhiteSpace(item.ProxyPwd))
					{
						myProxy.Credentials = new NetworkCredential(item.ProxyUserName, item.ProxyPwd);
					}
					//给当前请求对象
					HttpClient.DefaultProxy = myProxy;
				}
			}
		}

		/// <summary>
		/// 获取自动重试策略（Polly）
		/// </summary>
		private static AsyncRetryPolicy<HttpResponseMessage> GetRetryPolicy(int retryCount = 3)
		{
			return HttpPolicyExtensions
				.HandleTransientHttpError() // 5xx or timeout
				.WaitAndRetryAsync(retryCount, retryAttempt =>
					TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) // 指数退避
				);
		}
	}
}
